package edu.uvic.TabViz;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.FileReader;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import java.util.Random;

import android.app.Activity;
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView;
import android.inputmethodservice.KeyboardView.OnKeyboardActionListener;
import android.net.wifi.WifiManager;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.os.Handler;
import android.text.InputFilter;
import android.text.format.Formatter;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import edu.uvic.TabViz.framework.World;
import edu.uvic.TabViz.framework.WorldReplicate;
import edu.uvic.TabViz.framework.Objects.Cube;
import edu.uvic.TabViz.framework.WiFi.Data;
import edu.uvic.TabViz.framework.WiFi.Protocol;
import edu.uvic.TabViz.framework.primitives.Light;
import edu.uvic.TabViz.framework.primitives.Material;
import edu.uvic.TabViz.framework.primitives.Object3D;
import edu.uvic.TabViz.framework.primitives.Point3D;

public class TabVizActivity extends Activity implements OnKeyboardActionListener, OnKeyListener{

	int position = 0;
	protected RelativeLayout board;
	World thisWorld;
	protected FrameLayout main;
	public    RelativeLayout mirror;
	protected MainView mainView;

	protected EditText ipAddress;
	protected Button connect;
	protected KeyboardView keyboardView;

	protected ClientThread client;
	protected Server server;
	public    LinkedList<ServerThread> serverSockets ;
	protected Handler UIhandle;

	protected ImageButton grab, share, replicate, lock, reset;
	public boolean capture = false;
	HashMap<String, String> keyCodeMap = new HashMap<String, String>();

	protected ImageView mirroView;

	public boolean grabed = false;
	public boolean sharedServer = false;
	public boolean sharedClient = false;
	@Override
	public void onCreate(Bundle savedInstanceState) {
		keyCodeMap.put("1", "1");
		keyCodeMap.put("2", "2");
		keyCodeMap.put("3", "3");
		keyCodeMap.put("4", "4");
		keyCodeMap.put("5", "5");
		keyCodeMap.put("6", "6");
		keyCodeMap.put("7", "7");
		keyCodeMap.put("8", "8");
		keyCodeMap.put("9", "9");
		keyCodeMap.put("0", "0");
		keyCodeMap.put("-4", ".");

		requestWindowFeature(Window.FEATURE_NO_TITLE);
		getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
				WindowManager.LayoutParams.FLAG_FULLSCREEN);
		super.onCreate(savedInstanceState);

		setContentView(R.layout.main);
		board = (RelativeLayout) findViewById(R.id.board);
		main = (FrameLayout) findViewById(R.id.main);
		mirror = (RelativeLayout) findViewById(R.id.mirror);


		mainView = new MainView(this,null);	
		main.addView(mainView);

		keyboardView = (KeyboardView) findViewById(R.id.keyboard);
		Keyboard keyboard = new Keyboard(this, R.drawable.key);
		keyboardView.setKeyboard(keyboard);
		keyboardView.setEnabled(true);
		keyboardView.setPreviewEnabled(true);
		keyboardView.setOnKeyListener(this);
		keyboardView.setOnKeyboardActionListener(this);
		keyboardView.setVisibility(View.GONE);
		keyboardView.setPreviewEnabled(false);


		ipAddress = (EditText) findViewById(R.id.ip);
		ipAddress.setBackgroundColor(Color.BLACK);
		ipAddress.setText(getPeerAddress());

		InputFilter[] filters = new InputFilter[1];
		filters[0] = new InputFilter() {
			@Override
			public CharSequence filter(CharSequence source, int start, int end,
					android.text.Spanned dest, int dstart, int dend) {
				if (end > start) {
					String destTxt = dest.toString();
					String resultingTxt = destTxt.substring(0, dstart) + source.subSequence(start, end) + destTxt.substring(dend);
					if (!resultingTxt.matches ("^\\d{1,3}(\\.(\\d{1,3}(\\.(\\d{1,3}(\\.(\\d{1,3})?)?)?)?)?)?")) { 
						return "";
					} else {
						String[] splits = resultingTxt.split("\\.");
						for (int i=0; i<splits.length; i++) {
							try{
								if (Integer.valueOf(splits[i]) > 255) 
									return "";
							}catch (NumberFormatException nfe){
								nfe.printStackTrace();
							}
						}
					}
				}
				return null;
			}

		};
		ipAddress.setFilters(filters);
		ipAddress.setCursorVisible(true);
		ipAddress.setOnTouchListener(new View.OnTouchListener() {

			public boolean onTouch(View v, MotionEvent event) {
				toggleKeyboardVisibility(ipAddress);
				return true;
			}
		});

		connect = new Button(this);
		connect.setText("Connect");
		RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
		rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
		rlp.addRule(RelativeLayout.CENTER_IN_PARENT);
		connect.setLayoutParams(rlp);
		connect.setBackgroundColor(Color.BLACK);

		connect.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				try {
					client = new ClientThread(ipAddress.getText().toString());
					client.start();
				} catch (UnknownHostException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				} catch (NumberFormatException  e) {
					e.printStackTrace();
				}
			}
		});

		//		reset = (ImageButton) findViewById(R.id.resetButton);
		//		reset.setOnClickListener(new OnClickListener() {
		//			@Override
		//			public void onClick(View v) {
		//				reset();
		//			}
		//		});

		lock = (ImageButton) findViewById(R.id.lock);

		replicate = (ImageButton) findViewById(R.id.replicate);
		replicate.setEnabled(false);
		replicate.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				if(client != null){
					client.msgOut.add(new Data(Protocol.REPLICATE,Data.REQUEST));
				}
			}
		});

		share = (ImageButton) findViewById(R.id.share);
		share.setEnabled(false);
		share.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				if(client != null)
					if(!sharedClient)
						client.msgOut.add(new Data(Protocol.SHARE,Data.REQUEST));
					else if(sharedClient){
						client.msgOut.add(new Data(thisWorld.replicate(),Data.Release));
						client.comp.setState(Protocol.STATUS_READY); 
						grab.setVisibility(View.VISIBLE);
						mirror.setVisibility(View.VISIBLE);
						replicate.setVisibility(View.VISIBLE);
						share.setBackgroundDrawable(getResources().getDrawable(R.drawable.share));
						sharedClient = false;
					}else if(sharedServer){
						for (int i = 0; i<serverSockets.size(); i++){
							serverSockets.get(i).msgOut.add(new Data(thisWorld.replicate(),Data.Release));
							serverSockets.get(i).comp.setState(Protocol.STATUS_READY); 
						}
						grab.setVisibility(View.VISIBLE);
						mirror.setVisibility(View.VISIBLE);
						replicate.setVisibility(View.VISIBLE);
						share.setBackgroundDrawable(getResources().getDrawable(R.drawable.share));
						sharedServer = false;
					}
			}
		});

		grab = (ImageButton) findViewById(R.id.grab);
		grab.setEnabled(false);
		grab.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				if(client != null)
					if(!grabed)
						client.msgOut.add(new Data(Protocol.GRAB,Data.REQUEST));
					else if(grabed){
						client.msgOut.add(new Data(thisWorld.replicate(),Data.Release));
						client.comp.setState(Protocol.STATUS_READY);
						share.setVisibility(View.VISIBLE);
						mirror.setVisibility(View.VISIBLE);
						replicate.setVisibility(View.VISIBLE);
						grab.setBackgroundDrawable(getResources().getDrawable(R.drawable.grab));
						grabed = false;
					}
			}
		});

		mirror.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				switchCorners();
				position++;
				position%=4;
			}
		});
		mirror.addView(connect);

		mirroView = new ImageView(this);
		mirror.addView(mirroView);
		mirroView.setBackgroundColor(Color.BLACK);
		//		mirroView.setVisibility(View.INVISIBLE);

		Server srv = new Server();
		srv.start();
		onConfigurationChanged(this.getResources().getConfiguration());
		UIhandle = new Handler();
	}

	public void switchCorners(){
		RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(261,165);
		switch (position){
		case 1: 
			rlp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
			break;
		case 2:
			rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
			rlp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
			break;
		case 3:
			rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
			break;
		}
		mirror.setLayoutParams(rlp);
		mirror.invalidate();
	}
	private void toggleKeyboardVisibility(EditText ei) {
		if (keyboardView.getVisibility() == View.GONE)
			keyboardView.setVisibility(View.VISIBLE);
	}

	private CharSequence getPeerAddress() {
		BufferedReader br;
		String ip = "127.0.0.1";

		try {
			br = new BufferedReader(new FileReader("/proc/net/arp"));
			String line;
			br.readLine();
			while ((line = br.readLine()) != null) {
				String[] splitted = line.split(" +");
				ip = splitted[0];
			}
			br.close();
		}catch (IOException e) {
			Log.e("Client","Exception",e);
		}
		return ip;
	}

	public void reset(){
		thisWorld.getObjects().get(0).setToInit();
		for(Object3D o: thisWorld.getObjects().get(0).getObjectNodes()){
			o.setToInit();
		}
		mainView.requestRender();
		//		mainView.getRenderer().world.getCamera().getPosition().set(new float[]{-1,1,(float)- 2.5*(size * step + size * ep)});
		//		mainView.getRenderer().world.setBackground(0.0f, 0.3f, 0.0f, 0.0f);
	}

	public World createWorld(){		
		World world = new World();

		float ep = 1f;
		float size = 3;
		int step = 5;

		Light light = new Light();
		light.setDirection(2, 2, 12);
		light.setColor(.1f, .3f, .8f);
		light.setPosition(new Point3D(-size/2, size/2, -size*15));
		light.setIntensity(6);
		Random rand = new Random();
		float pSize = size*(step+ep)-ep;
		Object3D parent = new Cube(1,0,0,0);
		parent.setTouch(true);

		for(float x = pSize/2 ; x > -pSize/2; x-=step){
			for(float y = pSize/2 ; y > -pSize/2; y-=step){
				for(float z = pSize/2 ; z > -pSize/2; z-=step){
					Object3D object = new Cube(step, x, y, z);
					object.setMaterial(new Material());
//					object.setColor(x/(size * step + size * ep), y/(size * step + size * ep), z/(size * step + size * ep), 1.0f);
					object.setColor(rand.nextFloat(), rand.nextFloat(), rand.nextFloat(), 1.0f);
					object.setLight(light);
					parent.addNode(object);
					z-= ep;
				}
				y -= ep;
			}
			x -= ep;
		}
		world.addObject(parent);
		world.getCamera().getPosition().set(new float[]{-1,1,(float)- 3*(size * step + size * ep)});
		world.setmEyeZ(world.getCamera().getPosition().getZ());
		world.setBackground(0.0f, 0.0f, 0.0f, 0.0f);
		return world;
	}



	@Override
	protected void onResume() {
		super.onResume();
		mainView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
		mainView.getRenderer().setPause(false);
	}

	@Override
	protected void onPause() {
		super.onPause();
		mainView.getRenderer().setPause(true);

	}

	@Override
	protected void onStop() {
		super.onStop();
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		mainView.getRenderer().onSurfaceDestroyed();
		unbindDrawables(main);
		System.gc();
	}

	@Override
	public void onConfigurationChanged(Configuration newConfig) {
		super.onConfigurationChanged(newConfig);
		if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
			RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(newConfig.screenWidthDp/2, newConfig.screenHeightDp/2);
			rlp.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
			rlp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
			if (position >= 2)
				rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
			mirror.setLayoutParams(rlp);

			rlp = new RelativeLayout.LayoutParams(newConfig.screenWidthDp/2, newConfig.screenHeightDp/2);
			rlp.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
			rlp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
			if (position < 2)
				rlp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
			main.setLayoutParams(rlp);

		} else if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
			switchCorners();
			RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(newConfig.screenWidthDp, newConfig.screenHeightDp);
			rlp.addRule(RelativeLayout.CENTER_IN_PARENT);
			main.setLayoutParams(rlp);
		}
	}

	private void unbindDrawables(View view) {
		if (view.getBackground() != null) {
			view.getBackground().setCallback(null);
		}
		if (view instanceof ViewGroup) {
			for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
				unbindDrawables(((ViewGroup) view).getChildAt(i));
			}
			((ViewGroup) view).removeAllViews();
		}
	}



	public class Server extends Thread
	{
		boolean  listening = true;
		ServerSocket serverSocket = null;
		int port;
		public Server(){
			init();
		}
		public void run()
		{
			while (true){
				if (serverSocket != null){
					try {
						while ( listening ){
							ServerThread serverThread; 
							serverThread = new ServerThread( serverSocket.accept() );
							serverSockets.add(serverThread);
							serverThread.start();   /* --- Use for multy Threaded --- */							
							//listening = false;
						}
						serverSocket.close();
					}catch (IOException e) {
					}
				}
				else{
					init();
				}
			}
		}
		public void init(){
			WifiManager wifimanager=(WifiManager) mainView.getmContext().getSystemService(Context.WIFI_SERVICE);
			int ip = wifimanager.getConnectionInfo().getIpAddress();
			port = 6000 + Integer.parseInt(Formatter.formatIpAddress(ip).substring(Formatter.formatIpAddress(ip).lastIndexOf('.')+1));

			// --- Open the Server Socket where this should listen ---
			try {
				serverSocket = new ServerSocket( port );
				serverSockets = new LinkedList<TabVizActivity.ServerThread>();
			} catch (IOException e) {
			}
		}
	}

	public byte[] toBytes(Bitmap bm){
		ByteArrayOutputStream stream = new ByteArrayOutputStream();
		bm.compress(Bitmap.CompressFormat.PNG, 100, stream);
		return stream.toByteArray();
	}

	public Bitmap toBitmap(byte[] bytes){
		return  BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
	}
	class ServerThread extends Thread
	{
		private Socket clientSocket = null;
		Data data;
		ObjectInputStream oIn;
		ObjectOutputStream oOut;
		public volatile LinkedList<Data> msgOut = new LinkedList<Data>();
		public volatile LinkedList<Data> msgIn = new LinkedList<Data>();
		boolean eof = false;
		ServerProtocol comp;
		Listener t;
		/**
		 * -- Constructor --
		 */
		public ServerThread( Socket socket )
		{
			super( "ComServerThread" );
			this.clientSocket = socket;
		} 
		/**
		 * -- Override from the Thread (super) class --
		 */
		public void run()
		{
			try {
				oOut = new ObjectOutputStream( clientSocket.getOutputStream() );
				oIn  = new ObjectInputStream( clientSocket.getInputStream() );
				comp = new ServerProtocol(this);
				t = new Listener(this);
				t.start();
				comp.processInput( null ); //Sending Ready Signal
				Log.e("Server", "Ready Sent");
				while ( true )
				{
					if (!msgIn.isEmpty()){
						data = msgIn.removeFirst();
						comp.processInput(data);
					}
					if (!msgOut.isEmpty()){
						data = msgOut.removeFirst();
						Protocol.sendData(data, oOut);
					}
					if (eof)
						break;
				}
				Log.e("Server", "loop exit");
				// --- Close resources;  This client is gone ---
				oOut.close();
				oIn.close();
				clientSocket.close();
			} catch ( IOException e ) {
				Log.e("edu.uvic.TabViz","Client",e);
			}
		} 
	}

	class ServerProtocol extends Protocol{
		ServerThread server;
		Data input;
		public ServerProtocol(ServerThread server){
			this.server = server;
		}
		public void processInput( Data input){
			this.input = input;
			if ( state == STATUS_WAITING ){
				server.msgOut.add(new Data("Ready", Data.REQUEST));
				state = STATUS_READY;
			}
			if (state == STATUS_GRAB && input != null){
				if(input.type == Data.Touch) 
					mainView.onScroll(((float[])input.data)[0], ((float[])input.data)[1]);
				else if (input.type == Data.Release){
					UIhandle.post(new Runnable() {
						@Override
						public void run() {
							share.setVisibility(View.VISIBLE);
							grab.setVisibility(View.VISIBLE);
							replicate.setVisibility(View.VISIBLE);
							//							reset.setVisibility(View.VISIBLE);
							mirror.setVisibility(View.VISIBLE);
							lock.setBackgroundDrawable(TabVizActivity.this.getResources().getDrawable(R.drawable.unlock));
							main.removeView(mainView);
							mainView = new MainView(TabVizActivity.this, (WorldReplicate) ServerProtocol.this.input.data);
							main.addView(mainView);
							mainView.requestRender();
							mainView.grabTouch = false;
						}
					});	
					state = STATUS_READY;
				}
			}else if (state == STATUS_SHARE && input != null){
				if(input.type == Data.Touch) 
					mainView.onScroll(((float[])input.data)[0], ((float[])input.data)[1]);
				else if (input.type == Data.Release){
					UIhandle.post(new Runnable() {
						@Override
						public void run() {
							share.setBackgroundDrawable(TabVizActivity.this.getResources().getDrawable(R.drawable.share));
							grab.setVisibility(View.VISIBLE);
							replicate.setVisibility(View.VISIBLE);
							if(client == null)
								share.setEnabled(false);
							//							reset.setVisibility(View.VISIBLE);
							mirror.setVisibility(View.VISIBLE);
							main.removeView(mainView);
							mainView = new MainView(TabVizActivity.this, (WorldReplicate) ServerProtocol.this.input.data);
							main.addView(mainView);
							mainView.requestRender();
							sharedServer = false;
						}
					});	
					state = STATUS_READY;
				}
			}
			else if ( state == STATUS_READY && input != null){
				if(input.type == Data.REQUEST) {
					if(((Integer)input.data) == SYNC){
						mainView.lock = true;
						TabVizActivity.this.capture = true;
						mainView.requestRender();
						while(!mainView.done){
							try {
								Thread.sleep(5);
							} catch (InterruptedException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}
						server.msgOut.add(new Data(toBytes(mainView.mBitmap),Data.Syncronization));
						mainView.done = false;
						//						mainView.setEnabled(true);
					}
					if(((Integer)input.data) == REPLICATE){
						server.msgOut.add(new Data(thisWorld.replicate(),Data.Replication));
					}
					if(((Integer)input.data) == SHARE){
						sharedServer = true;
						server.msgOut.add(new Data(thisWorld.replicate(),Data.Share));
						UIhandle.post(new Runnable() {
							@Override
							public void run() {
								share.setBackgroundDrawable(TabVizActivity.this.getResources().getDrawable(R.drawable.fav));
								replicate.setVisibility(View.INVISIBLE);
								grab.setVisibility(View.INVISIBLE);
								if(client == null)
									share.setEnabled(true);
								//								reset.setVisibility(View.INVISIBLE);
								mirror.setVisibility(View.INVISIBLE);
							}
						});
						state = STATUS_SHARE;
					}
					if(((Integer)input.data) == GRAB){
						mainView.grabTouch = true;
						server.msgOut.add(new Data(thisWorld.replicate(),Data.Grab));
						UIhandle.post(new Runnable() {
							@Override
							public void run() {
								lock.setBackgroundDrawable(getResources().getDrawable(R.drawable.lock));
								share.setVisibility(View.INVISIBLE);
								replicate.setVisibility(View.INVISIBLE);
								grab.setVisibility(View.INVISIBLE);
								//								reset.setVisibility(View.INVISIBLE);
								mirror.setVisibility(View.INVISIBLE);
							}
						});
						state = STATUS_GRAB;
					}
				}
				else if(input.type == Data.RESPONSE){ 
					if (input.data.equals("Synced"))
						mainView.lock = false;
				}
			}
		} 
	}

	public class ClientThread extends Thread
	{
		private Socket comSocket;
		ClientProtocol comp;
		Data data;
		ObjectInputStream oIn;
		ObjectOutputStream oOut;
		String ip;
		int port;
		public volatile LinkedList<Data> msgOut = new LinkedList<Data>();
		public volatile LinkedList<Data> msgIn = new LinkedList<Data>();
		Listener t;
		boolean eof = false;
		/**
		 * -- Constructor --
		 */
		public ClientThread( String ip) throws UnknownHostException, IOException, NumberFormatException {
			port = 6000 + Integer.parseInt(ip.substring(ip.lastIndexOf('.')+1));
			this.ip =ip ;
		} 
		/**
		 * -- Override from the Thread (super) class --
		 */
		public void run()
		{
			try{
				if(comSocket == null){

					InetAddress serverAddress = InetAddress.getByName(ip);
					comSocket = new Socket( serverAddress, port );
					oOut = new ObjectOutputStream( comSocket.getOutputStream() ); 
					oIn = new ObjectInputStream( comSocket.getInputStream());
					comp = new ClientProtocol(this);
					t = new Listener(this);
					t.start();

				}
				while ( true )
				{
					if (!msgIn.isEmpty()){
						try{
							data = msgIn.removeFirst();
						}catch(NoSuchElementException e){
							Log.e("edu.uvic.TabViz","Client",e);
							continue;
						}
						comp.processInput(data);
					}
					if (!msgOut.isEmpty()){
						data = msgOut.removeFirst();
						Protocol.sendData(data, oOut);
						Log.e("Client",Integer.toString(data.type));
					}
					if (eof)
						break;
				}
				Log.e("Client Thread", "Close Shop");

				oOut.close();
				oIn.close();
				comSocket.close();
				UIhandle.post(new Runnable() {
					@Override
					public void run() {
						connect.setVisibility(View.VISIBLE);
						ipAddress.setVisibility(View.VISIBLE);
						mirroView.setVisibility(View.INVISIBLE);
						share.setEnabled(false);
						replicate.setEnabled(false);
						grab.setEnabled(false);
					}
				});			
			} catch ( java.net.UnknownHostException e ) {
				e.printStackTrace();
			}	catch (IOException e) {
				Log.e("edu.uvic.TabViz","Client",e);
			}	
		}  
	}

	class ClientProtocol extends Protocol
	{
		ClientThread client;
		Data input;
		public ClientProtocol(ClientThread client){
			this.client = client;
		}
		public void processInput( Data input)
		{
			this.input = input;

			if ( state == STATUS_WAITING && input != null){
				if (input.type == Data.REQUEST && input.data.equals("Ready")){
					client.msgOut.add(new Data(SYNC,Data.REQUEST));
					state = STATUS_READY;
				}
			}
			if (state == STATUS_GRAB){
				//ignore incoming till released
			}
			else if (state == STATUS_SHARE && input != null){
				if(input.type == Data.Touch) 
					mainView.onScroll(((float[])input.data)[0], ((float[])input.data)[1]);
				else if (input.type == Data.Release){
					UIhandle.post(new Runnable() {
						@Override
						public void run() {
							share.setBackgroundDrawable(TabVizActivity.this.getResources().getDrawable(R.drawable.share));
							grab.setVisibility(View.VISIBLE);
							replicate.setVisibility(View.VISIBLE);
							//							reset.setVisibility(View.VISIBLE);
							mirror.setVisibility(View.VISIBLE);
							main.removeView(mainView);
							mainView = new MainView(TabVizActivity.this, (WorldReplicate) ClientProtocol.this.input.data);
							main.addView(mainView);
							mainView.requestRender();
							sharedClient = false;
						}
					});	
					state = STATUS_READY;
				}
			}
			else if ( state == STATUS_READY && input != null){
				if(input.type == Data.Syncronization){ 
					UIhandle.post(new Runnable() {
						@Override
						public void run() {
							mirroView.setImageDrawable(null);
							connect.setVisibility(View.INVISIBLE);
							ipAddress.setVisibility(View.INVISIBLE);
							mirroView.setVisibility(View.VISIBLE);
							share.setEnabled(true);
							replicate.setEnabled(true);
							grab.setEnabled(true);
							mirroView.setImageBitmap(toBitmap((byte[])ClientProtocol.this.input.data));
							while(mirroView.getDrawable() == null){
								try {
									Thread.sleep(5);
								} catch (InterruptedException e) {
									// TODO Auto-generated catch block
									e.printStackTrace();
								}
							}
							mirror.invalidate();
						}
					});	

					client.msgOut.add(new Data("Synced",Data.RESPONSE));
				}
				else if(input.type == Data.Replication){
					UIhandle.post(new Runnable() {
						@Override
						public void run() {
							main.removeView(mainView);
							mainView = new MainView(TabVizActivity.this, (WorldReplicate) ClientProtocol.this.input.data);
							main.addView(mainView);
							mainView.requestRender();
						}
					});	
					Log.e("Client","Replicated");
				}
				else if (input.type == Data.Grab){
					grabed = true;
					UIhandle.post(new Runnable() {
						@Override
						public void run() {
							main.removeView(mainView);
							mainView = new MainView(TabVizActivity.this, (WorldReplicate) ClientProtocol.this.input.data);
							main.addView(mainView);
							mainView.requestRender();
							share.setVisibility(View.INVISIBLE);
							replicate.setVisibility(View.INVISIBLE);
							mirror.setVisibility(View.INVISIBLE);
							grab.setBackgroundDrawable(getResources().getDrawable(R.drawable.release));
						}
					});
					state = STATUS_GRAB;
				}
				else if (input.type == Data.Share){
					sharedClient = true;
					UIhandle.post(new Runnable() {
						@Override
						public void run() {
							main.removeView(mainView);
							mainView = new MainView(TabVizActivity.this, (WorldReplicate) ClientProtocol.this.input.data);
							main.addView(mainView);
							mainView.requestRender();
							grab.setVisibility(View.INVISIBLE);
							replicate.setVisibility(View.INVISIBLE);
							mirror.setVisibility(View.INVISIBLE);
							share.setBackgroundDrawable(getResources().getDrawable(R.drawable.fav));
						}
					});
					state = STATUS_SHARE;
				}
				else if(input.type == Data.Touch){ 
					UIhandle.post(new Runnable() {
						@Override
						public void run() {
							mirroView.setImageDrawable(null);
							mirroView.setImageBitmap(toBitmap((byte[])ClientProtocol.this.input.data));
							while(mirroView.getDrawable() == null){
								try {
									Thread.sleep(10);
								} catch (InterruptedException e) {
									// TODO Auto-generated catch block
									e.printStackTrace();
								}
							}
							mirror.invalidate();
						}
					});	
				}
			}
		}  
	}

	public class Listener extends Thread{
		Thread t;

		public Listener( Thread t) {
			this.t = t;
		} 

		public void run(){
			Data data;
			boolean done = false;
			while ( !done ){
				try {
					if(t instanceof ServerThread){
						data = Protocol.getData( ((ServerThread) t).oIn );
						((ServerThread) t).msgIn.add(data);
					} else if(t instanceof ClientThread){
						data = Protocol.getData( ((ClientThread) t).oIn );
						((ClientThread) t).msgIn.add(data);
					}
				}catch ( EOFException e ) {
					if(t instanceof ServerThread)
						((ServerThread) t).eof = true;
					else if(t instanceof ClientThread)
						((ClientThread) t).eof = true;
					done = true;
				}catch ( IOException e ) {
					Log.e("edu.uvic.TabViz","Client",e);
					continue;
				}
			}
		}  
	}

	@Override
	public boolean onKey(View arg0, int arg1, KeyEvent arg2) {
		return false;
	}

	@Override
	public void onKey(int primaryCode, int[] keyCodes) {
		String c = keyCodeMap.get(String.valueOf(primaryCode));
		if(!(c == null)){
			ipAddress.append(c);
		}
		else{
			switch(primaryCode){
			case -5:
				if(ipAddress.getText().toString().length() > 0)
					ipAddress.setText(ipAddress.getText().toString().substring(0, ipAddress.getText().toString().length() - 1));
				break;
			case -100:
				keyboardView.setVisibility(View.GONE);
				break;
			}
		}
	}

	@Override
	public void onPress(int arg0) {

	}

	@Override
	public void onRelease(int arg0) {
	}

	@Override
	public void onText(CharSequence arg0) {
	}

	@Override
	public void swipeDown() {
	}

	@Override
	public void swipeLeft() {
	}

	@Override
	public void swipeRight() {
	}

	@Override
	public void swipeUp() {
	}
}